package intopo.game.impl.pgp;

import java.io.PrintStream;
import org.w3c.dom.Element;
import java.util.Vector;

import util.xml.*;
import intopo.game.Game;

/**
 * This class represents a Public Good Provision game. In this game players
 * have two actions available: provide the good at some cost or do
 * nothing. The good that is provided is shared by all players
 * independently of who provided it or not. The more players that provide
 * the higher is the good value.

 * @author <a href="mailto:plsm@ua.pt">Pedro Lopes da Silva Mariano</a>

 * @version 1.0
 */
final public class PublicGoodProvisionGame
	implements Game<PublicGoodProvisionStrategy>
{
	/**
	 * 
	 */
	static String CONSTRUCTOR_TAG = "PublicGoodProvisionGame";
	/**
	 * How much it costs to provide the good. This should be a positive value.
	 */
	private float provisionCost;

	/**
	 * The value the public good has if all players provide. This should be
	 * a positive value.
	 */
	private float publicGoodValue;

	/**
	 * The number of players this game has.
	 */
	private int numberPlayers;

	final private float lowestPayoff;
	final private float highestPayoff;
	final private float paretoPayoff;

	public PublicGoodProvisionGame (float provisionCost, float publicGoodValue, int numberPlayers)
	{
		assert
			provisionCost >= 0
			&& publicGoodValue >= 0
			&& numberPlayers > 0
			&& publicGoodValue * (numberPlayers - 1) / numberPlayers > publicGoodValue - provisionCost : "With these parameters there is no dilemma";

		this.provisionCost = provisionCost;
		this.publicGoodValue = publicGoodValue;
		this.numberPlayers = numberPlayers;

		highestPayoff = publicGoodValue * (numberPlayers - 1) / numberPlayers;
		lowestPayoff = Math.min (0, publicGoodValue / numberPlayers - provisionCost);
		paretoPayoff = publicGoodValue - provisionCost;
	}

	public PublicGoodProvisionGame (Element element)
		throws XMLParseException
	{
		this
			(AttributeParser.get (0.0, Double.POSITIVE_INFINITY).parse (element, "provisionCost").floatValue (),
			 AttributeParser.get (0.0, Double.POSITIVE_INFINITY).parse (element, "publicGoodValue").floatValue (),
			 AttributeParser.get (2, Integer.MAX_VALUE).parse (element, "numberPlayers"));
// 		AttributeParser<Double> ap_double = new AttributeParser<Double>
// 			(new CheckExistenceAttribute<Double> (),
// 			 new ParseAsDouble (0, Double.POSITIVE_INFINITY));
// 		AttributeParser<Integer> ap_int = new AttributeParser<Integer>
// 			(new CheckExistenceAttribute<Integer> (),
// 			 new ParseAsInt (2, Integer.MAX_VALUE));

// 		AttributeParser<Double> ap_double = AttributeParser.get (0.0, Double.POSITIVE_INFINITY);
// 		AttributeParser<Integer> ap_int = AttributeParser.get (2, Integer.MAX_VALUE);

// 		provisionCost = ap_double.parse (element, "provisionCost").floatValue ();
// 		publicGoodValue = ap_double.parse (element, "publicGoodValue").floatValue ();
// 		numberPlayers = ap_int.parse (element, "numberPlayers");

// 		assert
// 			provisionCost >= 0
// 			&& publicGoodValue >= 0
// 			&& numberPlayers > 0
// 			&& publicGoodValue * (numberPlayers - 1) / numberPlayers > publicGoodValue - provisionCost : "With these parameters there is no dilemma";
	}
	public PublicGoodProvisionGame (java.util.Scanner scanner)
	{
		this (scanner.nextFloat (), scanner.nextFloat (), scanner.nextInt ());
	}

	public int numberPlayers ()
	{
		return numberPlayers;
	}

	public float lowestPayoff ()
	{
		return lowestPayoff;
	}

	public float highestPayoff ()
	{
		return highestPayoff;
	}

	public float paretoPayoff ()
	{
		return paretoPayoff;
	}

	/**
	 * Get the provision cost.
	 */
	public float getProvisionCost ()
	{
		return this.provisionCost;
	}
	/**
	 * Set the provision cost.
	 * @param provisionCost The new value of the <i>provision cost</i>.
	 */
	public void setProvisionCost (float provisionCost)
	{
		this.provisionCost = provisionCost;
	}
	/**
	 * Get the public good value.
	 */
	public float getPublicGoodValue ()
	{
		return this.publicGoodValue;
	}
	/**
	 * Set the public good value.
	 * @param publicGoodValue The new value of the <i>public good value</i>.
	 */
	public void setPublicGoodValue (float publicGoodValue)
	{
		this.publicGoodValue = publicGoodValue;
	}
	/**
	 * Set the number of players in the game.
	 */
	public void setNumberPlayers (int numberPlayers)
	{
		this.numberPlayers = numberPlayers;
	}
	public void play (Strategies<PublicGoodProvisionStrategy> players)
		throws IllegalArgumentException
	{
		int count = 0;
		float[] payoff = new float [numberPlayers];
		for (int i = 0; i < numberPlayers; i++) {
			if (players.getStrategy (i).provides ()) {
				payoff [i] = -provisionCost;
				count++;
			}
			else {
				payoff [i] = 0;
			}
		}
		for (int i = 0; i < numberPlayers; i++) {
			payoff [i] += publicGoodValue * count / numberPlayers;
			players.setPayoff (i, payoff [i]);
		}
	}

	public String toString ()
	{
		String s = "";
		s += "Public good value: " + publicGoodValue + "\n";
		s += "Provision cost:    " + provisionCost + "\n";
		s += "Number players:    " + numberPlayers + "\n";
		s += "Lowest payoff:     " + lowestPayoff () + "\n";
		s += "Highest payoff:    " + highestPayoff () + "\n";
		s += "#contributors\t | payoff contributor\t | payoff defector\n";
		s += "     0       \t |             NA    \t |        0\n"; 
		for (int i = 1; i < numberPlayers; i++) {
			float payoffContributor = publicGoodValue * i / numberPlayers - provisionCost;
			float payoffDefector = publicGoodValue * i / numberPlayers;

			s += "     " + i + "      \t |           " + payoffContributor + "\t |       " + payoffDefector + "\n";
		}
		s +=  "     " + numberPlayers + "      \t |           " + (publicGoodValue - provisionCost) + "\t |       NA\n";
		s += (publicGoodValue * (numberPlayers - 1) / numberPlayers)
			+ " <-->"
			+ (publicGoodValue - provisionCost) + "\n";
		if (publicGoodValue * (numberPlayers - 1) / numberPlayers <= publicGoodValue - provisionCost)
			s += "With these parameters there is no dilemma!!!\n";
		return s;
	}
	/**
	 *
	 * @param ps
	 */
	@Override
	public void write (PrintStream ps)
	{
		ps.print (PublicGoodProvisionGame.CONSTRUCTOR_TAG);
		ps.print (' ');
		ps.print (this.provisionCost);
		ps.print (' ');
		ps.print (this.publicGoodValue);
		ps.print (' ');
		ps.print (this.numberPlayers);
	}

}
